#!/bin/bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh

# Originally this was known as 'ignition-generator' found in ignition-dracut.
# With Ignition v 2.5.0, ignition-dracut was merged into Ignition and the nestos
# specific bits were deposited here.

set +euo pipefail
. /usr/lib/dracut-lib.sh
set -euo pipefail

# dracut isn't nice with `set -e`
dracut_func() {
    set +euo pipefail
    "$@"; local rc=$?
    set -euo pipefail
    return $rc
}

# Generators don't have logging right now
# https://github.com/systemd/systemd/issues/15638
exec 1>/dev/kmsg; exec 2>&1

UNIT_DIR="${1:-/tmp}"
EARLY_DIR="${2:-/tmp}"

IFS=" " read -r -a cmdline <<< "$(</proc/cmdline)"
cmdline_arg() {
    local name="$1" value=""
    for arg in "${cmdline[@]}"; do
        if [[ "${arg%%=*}" == "${name}" ]]; then
            value="${arg#*=}"
        fi
    done
    echo "${value}"
}

cmdline_bool() {
    local value
    value=$(cmdline_arg "$@")
    case "$value" in
        ""|0|no|off) return 1;;
        *) return 0;;
    esac
}

add_requires() {
    local name="$1"; shift
    local target="$1"; shift
    local requires_dir="${UNIT_DIR}/${target}.requires"
    mkdir -p "${requires_dir}"
    ln -sf "../${name}" "${requires_dir}/${name}"
}

# If this is a live image then exit early.
if is-live-image; then
    exit 0
fi

# Generate a sysroot.mount unit. We can't do this later by dynamically
# doing the mount like we used to because systemd 254+ will drop units that
# require sysroot.mount from the transaction:
# https://github.com/coreos/fedora-coreos-tracker/issues/1527#issuecomment-1656786452
#
# We'll dynamically pick up what mount options to use from the OPTIONS
# environment variable in /run/nestos-rootflags (generated by
# nestos-rootflags.service).
#
# Note that for new machines this is *only* useful on first boot because
# nestos-boot-edit.service will add the root= and rootflags= kargs to the
# kernel command line for subsequent boots. However we haven't migrated
# all old machines so there are still some that could exist that don't
# have root= and rootflags= kargs. For that reason we run this bit of
# code on EVERY diskful boot.
mkdir -p "${UNIT_DIR}"
# We want to generate sysroot.mount on ostree systems, but we don't want
# to run if there's already a root= karg, where the systemd-fstab-generator
# should win.
# We also don't want to generate sysroot.mount if we are booting a kdump kernel
# that aims to upload logs to a remote target, as we may be in a diskless setup
# https://issues.redhat.com/browse/OCPBUGS-27935
# FIXME use dracut-lib https://github.com/coreos/fedora-coreos-config/pull/2890#issuecomment-1985953499
# rather than ConditionPathExists
# Using dracut-lib seems to interfere with iscsi functionnality but this requires deeper investigation
# and more tests : https://github.com/coreos/fedora-coreos-tracker/issues/1689
if test -n "$(cmdline_arg ostree)" && test -z "$(cmdline_arg root)" && ! dracut_func getargbool 0 'kdump_remote_ip'; then
    cat >"${UNIT_DIR}"/sysroot.mount << 'EOF'
[Unit]
Before=initrd-root-fs.target
After=nestos-rootflags.service
[Mount]
EnvironmentFile=/run/nestos-rootflags
What=/dev/disk/by-label/root
Where=/sysroot
Options=${OPTIONS}
EOF
    add_requires sysroot.mount initrd-root-fs.target
    add_requires nestos-rootflags.service initrd-root-fs.target
fi

if ! cmdline_bool 'ignition.firstboot'; then
    exit 0
fi

# nestos-ignition-setup-user.service and `nestos-copy-firstboot-network.service`
# should depend on the boot device node only on diskful boots
mkdir -p "${UNIT_DIR}/nestos-ignition-setup-user.service.d"
mkdir -p "${UNIT_DIR}/nestos-copy-firstboot-network.service.d"
cat > "${UNIT_DIR}/nestos-ignition-setup-user.service.d/diskful.conf" <<EOF
[Unit]
Requires=dev-disk-by\x2dlabel-boot.device
After=dev-disk-by\x2dlabel-boot.device
EOF
cp "${UNIT_DIR}/nestos-ignition-setup-user.service.d/diskful.conf" \
    "${UNIT_DIR}/nestos-copy-firstboot-network.service.d/diskful.conf"

# If booting from iSCSI, then we need networking first before we
# can see the bootfs. This has some implications.
if test -n "$(cmdline_arg rd.iscsi.firmware)" || test -n "$(cmdline_arg netroot)"; then
    mkdir -p "${EARLY_DIR}/ignition-complete.target.requires"

    # In the injected Ignition config case, `nestos-ignition-setup-user.service`
    # can't "pass" the config to `ignition-fetch-offline.service`. So we neuter
    # the latter so that `ignition-fetch.service` (which runs after networking
    # comes up) can pick it up instead.
    ln -sf /dev/null "${EARLY_DIR}/ignition-complete.target.requires/ignition-fetch-offline.service"
    # activate ignition-fetch.service
    mkdir -p /run/ignition
    touch /run/ignition/neednet

    # With iSCSI, configuring networking via kargs is a hard requirement
    # since it can't be picked up from the bootfs (i.e. injected via
    # `--copy-network`). So neuter `nestos-copy-firstboot-network.service`.
    ln -sf /dev/null "${EARLY_DIR}/ignition-complete.target.requires/nestos-copy-firstboot-network.service"
fi

# create symlink for udev rule
mkdir -p /run/udev/rules.d/
ln -sf /usr/lib/nestos/80-nestos-boot-disk.rules \
      /run/udev/rules.d/80-nestos-boot-disk.rules

# IBM Secure Execution case
# During firstboot we have to reencrypt '/boot' and '/', to do that an Ignition config
# is injected. 'nestos-boot-disk' is required for this
secure_execution=0
if [[ $(uname -m) == s390x ]] && [[ -e /sys/firmware/uv/prot_virt_guest ]]; then
    secure_execution=$(cat /sys/firmware/uv/prot_virt_guest)
fi
if [[ "${secure_execution}" = "1" ]]; then
    mkdir -p /run/nestos/
    touch /run/nestos/secure-execution
    # Add dropins to disable Ignition logging for all stages
    stages=("fetch-offline" "fetch" "kargs" "disks" "mount" "files")
    for s in "${stages[@]}"; do
        dropin="${UNIT_DIR}/ignition-${s}.service.d"
        mkdir -p "${dropin}"
        cat > "${dropin}/10-secex.conf" <<EOF
[Service]
Environment=IGNITION_ARGS=-log-to-stdout
StandardOutput=null
StandardError=null
EOF
    done

    # This one is done dynamically because it hard Requires a device to appear
    # and if it's always part of the transaction, systemd will want the device
    # to appear regardless of ConditionPathExists.
    add_requires nestos-secex-ignition-prepare.service ignition-diskful.target
fi
